/* Copyright (c) 2023 Renmin University of China
RMDB is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
        http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

#pragma once

#include <cassert>
#include <cstring>
#include <memory>
#include <string>
#include <vector>

#include "execution/execution_defs.h"
#include "execution/execution_manager.h"
#include "record/rm.h"
#include "system/sm.h"
#include "common/context.h"
#include "plan.h"
#include "parser/parser.h"
#include "common/common.h"

class Planner {
   private:
    SmManager *sm_manager_;

    bool enable_nestedloop_join = true;
    bool enable_sortmerge_join = false;

   public:
    Planner(SmManager *sm_manager) : sm_manager_(sm_manager) {}


    std::shared_ptr<Plan> do_planner(std::shared_ptr<Query> query, Context *context);

    void set_enable_nestedloop_join(bool set_val) { enable_nestedloop_join = set_val; }
    
    void set_enable_sortmerge_join(bool set_val) { enable_sortmerge_join = set_val; }
    
   private:
    std::shared_ptr<Query> logical_optimization(std::shared_ptr<Query> query, Context *context);
    std::shared_ptr<Plan> physical_optimization(std::shared_ptr<Query> query, Context *context);

    std::shared_ptr<Plan> make_one_rel(std::shared_ptr<Query> query);

    std::shared_ptr<Plan> generate_sort_plan(std::shared_ptr<Query> query, std::shared_ptr<Plan> plan);

    std::shared_ptr<Plan> generate_groupby_plan(std::shared_ptr<Query> query, std::shared_ptr<Plan> plan);
    
    std::shared_ptr<Plan> generate_select_plan(std::shared_ptr<Query> query, Context *context);

    void adjust_conditions(const std::string& tab_name, std::vector<Condition>& curr_conds);

    CompOp swap_comp_op(CompOp op);

    std::pair<std::unordered_map<std::string, int>, std::unordered_map<std::string, int>> 
    parse_conditions(const std::vector<Condition>& curr_conds);

    int find_match_index(const TabMeta& tab, 
                                       const std::unordered_map<std::string, int>& eq_index_map, 
                                       const std::unordered_map<std::string, int>& neq_index_map);
    
    void populate_index_col_names(const TabMeta& tab, int index, std::vector<std::string>& index_col_names);

    std::pair<std::vector<int>, std::unordered_set<int>> get_match_indices(const TabMeta& tab, int index, 
                           const std::unordered_map<std::string, int>& eq_index_map, 
                           const std::unordered_map<std::string, int>& neq_index_map);

    void adjust_conditions_order(std::vector<Condition>& curr_conds, 
                                      const std::vector<int>& left_match_index, 
                                      const std::unordered_set<int>& left_match_index_set);

    // int get_indexNo(std::string tab_name, std::vector<Condition> curr_conds);
    bool get_index_cols(std::string tab_name, std::vector<Condition> curr_conds, std::vector<std::string>& index_col_names);

    bool get_both_index_cols(std::string& tab_name, std::vector<Condition>& join_conds);

    std::shared_ptr<ScanPlan> create_scan_plan(const std::string& tab_name, 
                                const std::vector<std::string>& index_col_names);

    ColType interp_sv_type(ast::SvType sv_type) {
        std::map<ast::SvType, ColType> m = {
            {ast::SV_TYPE_INT, TYPE_INT}, {ast::SV_TYPE_FLOAT, TYPE_FLOAT}, {ast::SV_TYPE_STRING, TYPE_STRING}};
        return m.at(sv_type);
    }
};
